package com.cqjtu.education.utils;

import com.aliyun.oss.*;
import com.aliyun.oss.model.*;
import com.cqjtu.education.dao.CourseDataDao;
import com.cqjtu.education.entity.CourseData;
import com.cqjtu.education.service.CourseDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.net.URLDecoder;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;


/**
 * @Author: 冉鹏
 * @Date: 2022/4/15 17:51
 */
@Component
@Slf4j
public class AliyunOSS {

    private final String endPoint = "oss-cn-beijing.aliyuncs.com";

    private final String bucketName = "cqjtu-education";

    private final String accessKeyId = "LTAI5t82vdtTVaiF1hBFrvb9";

    private final String accessKeySecret = "DOfLZoA5MjJcmesowrBiLYWYVTR7tE";


    @Resource
    private CourseDataDao courseDataDao;

    @Resource
    private CodeUtil codeUtil;


    /**
     * 将文件大小转为B、KB、MB、GB
     *
     * @param size 文件大小
     * @return 单位
     */
    public String getSize(long size) {
        StringBuffer bytes = new StringBuffer();
        DecimalFormat format = new DecimalFormat("###.0");
        if (size >= 1024 * 1024 * 1024) {
            double i = (size / (1024.0 * 1024.0 * 1024.0));
            bytes.append(format.format(i)).append("GB");
        } else if (size >= 1024 * 1024) {
            double i = (size / (1024.0 * 1024.0));
            bytes.append(format.format(i)).append("MB");
        } else if (size >= 1024) {
            double i = (size / (1024.0));
            bytes.append(format.format(i)).append("KB");
        } else if (size < 1024) {
            if (size <= 0) {
                bytes.append("0B");
            } else {
                bytes.append((int) size).append("B");
            }
        }
        return bytes.toString();
    }

    /**
     * @return OSS实体对象
     */
    public OSS newObject() {
        return new OSSClientBuilder().build(this.endPoint, this.accessKeyId, this.accessKeySecret);
    }


    /**
     * 扫描该路径下是否有当前想要修改的文件名
     *
     * @param objectName 路径
     * @param newName    修改的文件名
     * @return true存在 false不存在
     */
    public boolean scan(String objectName, String newName) {
        List<CourseData> result = courseDataDao.queryByParentPath(objectName);
        try {
            for (CourseData courseData : result) {
                String s = courseData.getFileName();
                if (s.replaceAll(objectName, "").equals(newName)) {
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 文件重命名 存在则加（x） x=1、2、3... 直至遍历出不存在的名字
     *
     * @param objectName 路径
     * @return 是否修改成功
     */
    public String reNameFile(String parent, String objectName, String newName) {
        OSS ossClient = newObject();
        //父目录
        boolean found = scan(parent, newName);
        if (!found) {
            try {
                // 将examplebucket下的srcobject.txt拷贝至同一Bucket下的destobject.txt
                ossClient.copyObject(bucketName, objectName, bucketName, parent + newName);

                // 删除srcobject.txt。
                ossClient.deleteObject(bucketName, objectName);
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
                return "";
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
                return "";
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
        return parent + newName;
    }


    /**
     * 新建文件夹
     *
     * @param objectName 路径,以/结尾
     * @return 是否成功
     */
    public String newFolder(String objectName) {
        OSS ossClient = newObject();
        ObjectMetadata objectMeta = new ObjectMetadata();
        byte[] buffer = new byte[0];
        ByteArrayInputStream in = new ByteArrayInputStream(buffer);
        objectMeta.setContentLength(0);
        try {
            ossClient.putObject(this.bucketName, objectName + "/", in, objectMeta);
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        } finally {
            ossClient.shutdown();
        }
        return objectName;
    }


    /**
     * 上传文件设置公共读
     *
     * @param multipartFile 文件
     * @return url
     */
    public String uploadPublic(MultipartFile multipartFile) {
        OSS ossClient = newObject();
        try {
            ObjectMetadata meta = new ObjectMetadata();
            InputStream inputStream = multipartFile.getInputStream();
            meta.setObjectAcl(CannedAccessControlList.PublicRead);
            String str = "yyyy-MM-dd HH:mm:ss";
            SimpleDateFormat sdf = new SimpleDateFormat(str);
            String fileName = "course_cover/" + codeUtil.generateVerifyCode(4) + "-" + sdf.format(new Date()) + "-" + multipartFile.getOriginalFilename();
            String url = "https://" + this.bucketName + "." + this.endPoint + "/" + fileName;
            ossClient.putObject(this.bucketName, fileName, inputStream, meta);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ossClient.shutdown();
        }
        return "";
    }

    public String getUrl(String objectName) {
        OSS ossClient = newObject();
        //设置URL过期时间为一小时
        Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
        //generatePresignedUrl
        String url = ossClient.generatePresignedUrl(this.bucketName, objectName, expiration).toString();
        System.out.println("url:" + url);
        return url;
    }

    //上传文件
    public String upload(CourseData courseData, MultipartFile multipartFile) {
        OSS ossClient = newObject();
        try {
//            ObjectMetadata meta = new ObjectMetadata();
            InputStream inputStream = multipartFile.getInputStream();
            // 指定上传的内容类型。
            //meta.setContentType("text/plain");

            // 文件上传时设置访问权限ACL。
            // meta.setObjectAcl(CannedAccessControlList.Private);
            //课程名+路径 最后要带/
            System.out.println(courseData.toString());
            String fileName = courseData.getParentPath() + multipartFile.getOriginalFilename();
            String url = "https://" + this.bucketName + "." + this.endPoint + "/" + fileName;
            // 通过UploadFileRequest设置多个参数。
            // 依次填写Bucket名称（例如examplebucket）以及Object完整路径（例如exampledir/exampleobject.txt），Object完整路径中不能包含Bucket名称。
//            UploadFileRequest uploadFileRequest = new UploadFileRequest(this.bucketName,url);
//            UploadFileRequest uploadFileRequest = new UploadFileRequest(this.bucketName,fileName);
            // 通过UploadFileRequest设置单个参数。
            // 填写本地文件的完整路径，例如D:\\localpath\\examplefile.txt。如果未指定本地路径，则默认从示例程序所属项目对应本地路径中上传文件。
            ossClient.putObject(this.bucketName, fileName, inputStream);
            //上传后需读入数据库，设置相关字段
            courseData.setDataId(UUID.randomUUID().toString());
            courseData.setFileName(multipartFile.getOriginalFilename());
            courseData.setFileSuffix(multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".") + 1));
            courseData.setFile(true);
            courseData.setFileSize(getSize(multipartFile.getSize()));
            courseData.setDataUrl(url);
            courseData.setCreateTime(new Date());
            courseDataDao.insert(courseData);
            System.out.println(courseData.toString());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            // 关闭OSSClient。
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return courseData.getDataUrl();
    }

    //下载文件
    public String download(OutputStream os, String objectName) {
        OSS ossClient = newObject();
//        OSS ossClient=ossConfig.ossClient();
//        System.out.println(ossClient.doesObjectExist(this.bucketName, objectName));

        try {
//            boolean found = ossClient.doesObjectExist(this.bucketName, objectName);

            OSSObject ossObject = ossClient.getObject(this.bucketName, objectName);
            // 读取文件内容。
            //oss文件
            BufferedInputStream in = new BufferedInputStream(ossObject.getObjectContent());
            //下载到浏览器指定位置的文件
            BufferedOutputStream out = new BufferedOutputStream(os);
            //缓冲区
            byte[] buffer = new byte[1024];
            int lenght = 0;
            //将输入流中读取一定数量 并将其存储在缓冲区数组 buffer 中
            while ((lenght = in.read(buffer)) != -1) {
                //将缓存区内容读入out
                out.write(buffer, 0, lenght);
            }
            if (out != null) {
                out.flush();
                out.close();
            }
            if (in != null) {
                in.close();
            }
            // ossObject对象使用完毕后必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
            ossObject.close();

        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
            return "下载失败";
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
            return "下载失败";
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return "下载成功";
    }

    //单个删除文件
    public String delete(String objectName) {
        OSS ossClient = newObject();
        try {
            // 删除文件或目录。如果要删除目录，目录必须为空。
            ossClient.deleteObject(bucketName, objectName);
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
            return "删除失败";
        }finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return "删除成功";
    }

    //批量删除文件
    public String batchDelete(List<String> objectName) {
        OSS ossClient = newObject();
        try {
            // 删除文件。
            // 填写需要删除的多个文件完整路径。文件完整路径中不能包含Bucket名称。
            DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(objectName).withEncodingType("url"));
            List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
            try {
                for (String obj : deletedObjects) {
                    String deleteObj = URLDecoder.decode(obj, "UTF-8");
                    System.out.println(deleteObj);
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
            return "删除失败";
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return "删除成功";
    }

    //删除指定目录及其所属文件
    public String deleteFolder(String objectName){
        // 填写不包含Bucket名称在内的目录完整路径。例如Bucket下testdir目录的完整路径为testdir/。
        //String prefix = "testdir/";

        OSS ossClient = newObject();
        try {
            // 删除目录及目录下的所有文件。
            String nextMarker = null;
            ObjectListing objectListing = null;
            do {
                ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
                        .withPrefix(objectName)
                        .withMarker(nextMarker);

                objectListing = ossClient.listObjects(listObjectsRequest);
                if (objectListing.getObjectSummaries().size() > 0) {
                    List<String> keys = new ArrayList<>();
                    for (OSSObjectSummary s : objectListing.getObjectSummaries()) {
                        System.out.println("key name: " + s.getKey());
                        keys.add(s.getKey());
                    }
                    DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName).withKeys(keys).withEncodingType("url");
                    DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(deleteObjectsRequest);
                    List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
                    try {
                        for(String obj : deletedObjects) {
                            String deleteObj =  URLDecoder.decode(obj, "UTF-8");
                            System.out.println(deleteObj);
                        }
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }

                nextMarker = objectListing.getNextMarker();
            } while (objectListing.isTruncated());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
            return "删除失败";
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return "删除成功";
    }

}
